#!/usr/bin/perl

######################################################################
package Swignition::uF::adr;
######################################################################

use CGI::Util;
use Swignition::GenericParser::Utils;
use Swignition::uF;
use XML::LibXML qw(:all);
use strict;

sub consume
{
	my $page = shift;

	unless ( $page->{Profiles}->{'http://www.w3.org/2006/03/hcard'}
	||       $page->{Profiles}->{'http://purl.org/uF/hCard/1.0/'}
	||       $page->{Profiles}->{'http://purl.org/uF/2008/03/'}
	||      !$page->{options}->{uf_strict_profiles})
	{
		return;
	}
		
	$page->{uF}->{adr} = [ parse_all($page) ];
	foreach my $adr (@{ $page->{uF}->{adr} })
		{ $page->mergeSubject( $adr->rdf_subject ); }
	if (defined $page->{uF}->{adr}->[0])
		{ $page->mergeNS( $page->{uF}->{adr}->[0] ); }
}

sub parse_all
{
	my $page   = shift;
	my $within = shift || $page->{DOM};
	my @nodes = searchClass('adr', $within);
	my @rv;
	
	foreach my $adr (@nodes)
	{
		my $adr_parsed = parse($page, $adr);
		push @rv, $adr_parsed;
	}
	
	return @rv;
} #/sub parse_all


sub uri
{
	my $this = shift;
	my $all  = shift;
	my @rv;
	
	if (length $this->{_id})
	{
		push @rv, Swignition::uF::TDBURI($this->{_page}->uri.'#'.$this->{_id});
	}
	if (lc($this->{_dom}->tagName) eq 'body')
	{
		push @rv, Swignition::uF::TDBURI($this->{_page}->uri);
	}
	push @rv, Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'Address')
		unless @rv;
	
	return $rv[0] unless (defined $all);
	while ($all) { shift @rv; $all--; }
	return @rv;
}


sub metadata_ns
{
	my $this = shift;
	return {
		'v'         => { nsurl=>'http://www.w3.org/2006/vcard/ns#',title=>'vCard' },
		'vx'        => { nsurl=>'http://buzzword.org.uk/rdf/vcardx#',title=>'vCard Extensions' }
		};
} #/sub namespace


sub rdf_subject
{
	my $this    = shift;
	my $ns      = 'http://www.w3.org/2006/vcard/ns#';

	# Create RDF subject
	my $subject = Swignition::RDFModel::Subject->new($this->uri);
	$subject->addObject($this);
	$subject->setDom($this->{_dom});

	$subject->addVal('http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
		Swignition::RDFModel::Value->new($ns.'Address', 1));

	# Patterns for simple data.
	my @_MULTIS   = qw(post-office-box extended-address street-address locality
		region postal-code country-name type);
	
	# Properties which exist as multiple values.
	foreach my $property (@_MULTIS)
	{
		foreach my $V (@{ $this->{$property} })
		{
			next unless (length $V);
			$subject->addVal((($property eq 'type') ? 'http://buzzword.org.uk/rdf/vcardx#usage' : $ns.$property),
				Swignition::RDFModel::Value->new($V));
		}
	}
	
	# Geo
	foreach my $V (@{ $this->{geo} })
	{
		my $val;
		if (ref $V)
		{
			$val = Swignition::RDFModel::Value->new($V->uri, 1);
			my $gSubject = Swignition::RDFModel::Subject->new($V->uri);
			$gSubject->addVal($ns.'adr',
				Swignition::RDFModel::Value->new($this->uri, 1));
			$this->{_page}->mergeSubject($gSubject);
		}
		else
			{ $val = Swignition::RDFModel::Value->new($V); }
		$subject->addVal($ns.'geo', $val);
	}
	
	return $subject;
	
} #/sub rdf_subject


sub parse
{
	my $page = shift;
	my $rv = { '_dom'=>shift };
	my $pkg = __PACKAGE__;
	if (defined $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') })
		{ return $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') }; }
	else
		{ $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') } = $rv; }
	
	my $adr_node = $rv->{'_dom'}->cloneNode(1);

	my $id = $adr_node->getAttribute('id');
	$rv->{_id} = $id if (length $id);
	$rv->{_page} = $page;
	
	data_patterns($page, $adr_node);
	
	simple_parse(
		$rv,
		$adr_node,
		[
			['geo', 'm*', {'embedded'=>'geo'}], # extension to the spec
			['post-office-box',  '*'],
			['extended-address', '*'],
			['street-address',   '*'],
			['locality',         '*'],
			['region',           '*'],
			['postal-code',      '*'],
			['country-name',     '*'],
			['type',             '*']  # only allowed when used in hCard. still...
		],
		{
			'no-destroy' => ['geo']
		}
	);

	bless $rv;
	return $rv;
	
} #/sub parse


sub validate
{
	my $this = shift;
	my @rv;

	push @rv, ['adr', $this->uri, 'W', 1, "No street-address or post-office-box.", "It is not likely that this address could be used for delivery."]
		if (!$this->{'street-address'} && !$this->{'post-office-box'});

	push @rv, ['adr', $this->uri, 'W', 2, "No locality.", "It is not likely that this address could be used for delivery."]
		if (!$this->{'locality'});

	push @rv, ['adr', $this->uri, 'W', 3, "No country-name.", "On the web it is usually best to explicitly include the country within the address."]
		if (!$this->{'country-name'});

	if ((defined $this->{'country-name'}->[0]) && $this->{'country-name'}->[0] =~ /^\s*[a-z]{2,3}\s*$/i)
	{
		push @rv, ['adr', $this->uri, 'W', 4, "Possibly invalid country-name.", "The country-name is supposed to include the full name of the country, not the two or three letter ISO code."]
			unless $this->{'country-name'}->[0] =~ /^\s*u(sa|k|ae)\s*$/i;
	}

	push @rv, ['adr', $this->uri, 'W', 5, "Second country-name.", "Although there are some cases where it makes sense to include a second country-name within an adr (e.g. Scotland, UK; England, UK; etc), this is usually a mistake, and might not be supported by parsers."]
		if (defined $this->{'country-name'}->[1]);

	push @rv, ['adr', $this->uri, 'W', 6, "Second postal-code.", "Although there are some postal systems where this may make sense, it's usually a mistake, so I'm flagging it up for you."]
		if (defined $this->{'postal-code'}->[1]);
	
	return @rv;
}


1; #/package Swignition::uF::adr

